package com.pic.mgt.syushin.manager.terminal;

import java.io.File;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.dao.DataAccessException;

import com.pic.mgt.dao.ITerminalMgtDao;
import com.pic.mgt.log.IgxLog;
import com.pic.mgt.manager.terminal.BasicTerminalInfoBean;
import com.pic.mgt.manager.terminal.SimpleTerminalManager;
import com.pic.mgt.manager.terminal.TerminalManagementOperationException;
import com.pic.mgt.monitor.physicalmonitor.FileMonitor;
import com.pic.mgt.monitor.physicalmonitor.FileMonitorException;
import com.pic.mgt.monitor.physicalmonitor.FileMonitorStopException;
import com.pic.mgt.monitor.physicalmonitor.MonitoredFile;
import com.pic.mgt.monitor.physicalmonitor.FileMonitor.FileMonitorEvent;

public final class SimpleSyushinTerminalManager extends
		SimpleTerminalManager implements SyushinTerminalManager {
	
	private TerminalFolderDetection terminalFolderDetection;
	
	private FileMonitor fileMonitor;
	
	public SimpleSyushinTerminalManager(ITerminalMgtDao terminalMgtDao,
									    String haishinOutboxPathStr,
										int terminalIdLen,
										FileMonitor fileMonitor) 
									throws TerminalManagementOperationException {
		super(terminalMgtDao, haishinOutboxPathStr, terminalIdLen);
		
		this.fileMonitor = fileMonitor;
		
		
		// 啟動端末目錄監控(以動態偵測端末是否新增或移除)
		try {
			// throws FileMonitorException
			this.terminalFolderDetection = new TerminalFolderDetection();
			// throws FileMonitorException
			this.terminalFolderDetection.startTerminalFolderDetection();
		} catch (FileMonitorException e) {
			try {
				throw new TerminalManagementOperationException(
						"SUDA-319 Starting monitor terminal folder (under haishin Outbox) failed: " + e.getMessage());
			} catch (TerminalManagementOperationException e1) {
				// ********************** Log **********************
				IgxLog.messageLog.error(this, e1.getMessage());
				// ********************** Log **********************
				
				throw e1;
			}
		}
	}
	
	@Override
	protected void synchronizeAddTerminals(Map<String, Object> terminalFolderIdMap,
			  							   Map<String, BasicTerminalInfoBean> basicTerminalInfoBeanInDbMap)
										   throws TerminalManagementOperationException {
		// 以資料庫中的有效端末為主。若端末不存在於清單中且有對應的目錄，則必須新增。
		
		Set<String> terminalIdStrInDbSet = basicTerminalInfoBeanInDbMap.keySet();
		for (String terminalIdStrInDb : terminalIdStrInDbSet) {
			if (super.basicTerminalInfoBeanMap.containsKey(terminalIdStrInDb)) {
				// 如果清單內有該端末，則只要進行 BasicTerminalInfoBean 內容的更新
				
				super.updateBasicTerminalInfoBean(super.basicTerminalInfoBeanMap.get(terminalIdStrInDb),
												  basicTerminalInfoBeanInDbMap.get(terminalIdStrInDb));
			}
			else {
				// 若清單內沒有該端末，若此端末為有效端末且有對應的目錄存在，則必須新增。
				
				if (super.isEffectiveTerminalId(basicTerminalInfoBeanInDbMap.get(terminalIdStrInDb)) &&
					terminalFolderIdMap.containsKey(terminalIdStrInDb)) {
					doAddTerminal(basicTerminalInfoBeanInDbMap.get(terminalIdStrInDb));
					
					// 同步新增"後"通知 TerminalListener
					this.terminalManagementListener.
							afterSynchronizeAddedTerminalNotify(basicTerminalInfoBeanInDbMap.get(terminalIdStrInDb));
				}
			}
			
		}
	}

	@Override
	protected void synchronizeRemoveTerminals(Map<String, Object> terminalFolderIdMap,
											  Map<String, BasicTerminalInfoBean> basicTerminalInfoBeanInDbMap)
											  throws TerminalManagementOperationException {
		// 以清單中的有效端末為主。若端末不存在於資料庫中，則必須刪除。
		
		// 用來存放將要移除的端末ＩＤ
		List<String> toBeRemovedTerminalIdStrList = new LinkedList<String>();
		
		Set<String> terminalIdStrSet = super.basicTerminalInfoBeanMap.keySet();
		for (String terminalIdStr : terminalIdStrSet) {
			if (!basicTerminalInfoBeanInDbMap.containsKey(terminalIdStr)) {
				// 先將要移除的端末ＩＤ放到List
				toBeRemovedTerminalIdStrList.add(terminalIdStr);
			}
		}
		
		// 開始做移除
		for (String toBeRemovedTermainlIdStr : toBeRemovedTerminalIdStrList) {
			// 要刪除"前"先通知 TerminalListener
			this.terminalManagementListener.
					beforeSynchronizeRemovedTerminalNotify(toBeRemovedTermainlIdStr);
			
			doRemoveTerminal(toBeRemovedTermainlIdStr);
		}
	}
	
	@Override
	public synchronized void addTerminal(String newTerminalId)
			throws TerminalManagementOperationException {
		// 集信只需要做的是新增至端末清單
		
		// 從資料庫中取出該已新增的端末基本資訊
		BasicTerminalInfoBean newBasicTerminalInfoBean;
		try {
			// throws DataAccessException
			newBasicTerminalInfoBean = super.terminalMgtDao.
							findTerminalBasicInfoByTerminalId(newTerminalId);
		} catch (DataAccessException e) {
			// ********************** Log **********************
			IgxLog.messageDetailsLog.error(this, e.getMessage());
			// ********************** Log **********************
			
			throw new TerminalManagementOperationException(
					"SUDA-317 Adding terminal '" + newTerminalId + "'failed. Due to database operation error." ,e);
		}
		if (null == newBasicTerminalInfoBean) {
			throw new TerminalManagementOperationException(
					"SUDA-318 Adding terminal '" + newTerminalId + "' failed. Can't find new terminal '" +
					newTerminalId + "' in database (table: " +
					super.terminalMgtDao.getTableName() + ")");
		}
		
		doAddTerminal(newBasicTerminalInfoBean);
		
		// 動態新增"後"通知 TerminalListener
		this.terminalManagementListener.
				afterDynamicAddedTerminalNotify(newBasicTerminalInfoBean);
	}
	
	private void doAddTerminal(BasicTerminalInfoBean newBasicTerminalInfoBean) {
		// 新增至端末清單
		synchronized(super.basicTerminalInfoBeanMap) {
			super.basicTerminalInfoBeanMap.put(newBasicTerminalInfoBean.getTerminalId(),
											   newBasicTerminalInfoBean);
		}
		
		// ********************** Log **********************
		IgxLog.messageLog.info(this,
				"Creating terminal '{}' operation completed.",
				newBasicTerminalInfoBean.getTerminalId());
		// ********************** Log **********************
	}
	
	@Override
	public synchronized void removeTerminal(String toBeRemovedTerminalIdStr)
			throws TerminalManagementOperationException {
		// 集信只需要做的是從有效端末清單中移除
		
		// 要刪除"前"先通知 TerminalListener
		this.terminalManagementListener.
				beforeDynamicRemovedTerminalNotify(toBeRemovedTerminalIdStr);
		
		doRemoveTerminal(toBeRemovedTerminalIdStr);
	}
	
	private void doRemoveTerminal(String toBeRemovedTerminalIdStr) {
		// 從端末清單中移除
		synchronized (super.basicTerminalInfoBeanMap) {
			super.basicTerminalInfoBeanMap.remove(toBeRemovedTerminalIdStr);
		}
		
		// ********************** Log **********************
		IgxLog.messageLog.info(this,
				"Removing terminal '{}' operation completed.", toBeRemovedTerminalIdStr);
		// ********************** Log **********************
	}
	
	
	
	/**
	 * 此類別是集信系統用來偵測端末目錄的新增或移除,以便能夠得知端末已被新增或移除(因為端末
	 * 的管理動作皆是在配信系統上操作)。也就是集信系統要能夠即時得知端末的異動，必須要藉由對
	 * 端末資料匣的監控來達成。
	 * 1. 當端末資料匣被新增出來，表示配信系統完成了一項端末的新增作業。
	 * 2. 當端末資料匣被刪除，表示配信系統完成了一項端末的移除作業。
	 * @author p10118178
	 *
	 */
	private class TerminalFolderDetection implements Runnable {
		private boolean isStartedMonitor = false;
		
		public TerminalFolderDetection() throws FileMonitorException {
			// 監控目錄的'建立'和'刪除'事件
			fileMonitor.setMonitorEvent(FileMonitorEvent.FILE_CREATE, FileMonitorEvent.FILE_DELETE);
			
			// 新增目錄監控
			// throws FileMonitorException
			fileMonitor.addMonitorPath(haishinOutboxPathStr);
		}
		
		public void startTerminalFolderDetection() throws FileMonitorException  {
			if (isStartedMonitor) {
				return;
			}
			
			// 開始監控
			// throws FileMonitorException
			fileMonitor.startMonitor();
			
			// 啟動 thread 讀取監控到的檔案變動
			new Thread(this).start();
			
			isStartedMonitor = true;
			
			// ********************** Log **********************
			IgxLog.messageLog.info(SimpleSyushinTerminalManager.this,
					"Started monitor terminal folder (under haishin Outbox).");
			// ********************** Log **********************
		}

		@Override
		public void run() {
			try {
				while(true) {
					List<MonitoredFile> monitoredFileList = fileMonitor.readMonitoredList();
					if (!monitoredFileList.isEmpty()) {
						for (MonitoredFile monitoredFile : monitoredFileList) {
							File detectedFile = monitoredFile.getChangedFile();
							FileMonitorEvent detectedEvent = monitoredFile.getFileChangedEvent();
							
							if (monitoredFile.isDirectory()) {
								String detectedTerminalId = detectedFile.getName().trim();
								
								if (detectedEvent == FileMonitorEvent.FILE_CREATE) {
									if (detectedTerminalId.length() == terminalIdLen) {
										// ********************** Log **********************
										IgxLog.messageLog.info(this,
														"Detected a terminal folder '{}' creation.",
														detectedFile.getAbsolutePath());
										// ********************** Log **********************
										
										// 做新增端末動作
										try {
											// throws TerminalManagementOperationException
											addTerminal(detectedTerminalId);
										} catch (TerminalManagementOperationException e) {
											// ********************** Log **********************
											IgxLog.messageLog.error(
															SimpleSyushinTerminalManager.this,
															e.getMessage());
											// ********************** Log **********************
										}
									}
									else {
										// 端末 id 長度不正確
										
										// ********************** Log **********************
										IgxLog.messageLog.warn(this,
														"Detected a terminal folder '{}' creation. But the length of terminal is incorrect.",
														detectedFile.getAbsolutePath());
										// ********************** Log **********************
									}
								}
								else if (detectedEvent == FileMonitorEvent.FILE_DELETE) {
									if (detectedTerminalId.length() == terminalIdLen) {
										// ********************** Log **********************
										IgxLog.messageLog.info(this,
														"Detected a terminal folder '{}' deletion.",
														detectedFile.getAbsolutePath());
										// ********************** Log **********************
										
										// 做移除端末動作
										try {
											// throws TerminalManagementOperationException
											removeTerminal(detectedTerminalId);
										} catch (TerminalManagementOperationException e) {
											// ********************** Log **********************
											IgxLog.messageLog.error(
															SimpleSyushinTerminalManager.this,
															e.getMessage());
											// ********************** Log **********************
										}
									}
									else {
										// 端末 id 長度不正確
										
										// ********************** Log **********************
										IgxLog.messageLog.warn(this,
														"Detected a terminal folder '{}' deletion. But the length of terminal is incorrect.",
														detectedFile.getAbsolutePath());
										// ********************** Log **********************
									}
								}
							}
							else {
								// 檔案不做處理
							}
						}
					}
				}
			} catch (FileMonitorStopException e) {
				// ********************** Log **********************
				IgxLog.messageLog.info(this, "Stopped monitor terminal folder (under haishin Outbox).");
				// ********************** Log **********************
			}
		}
	}
}
